/**
 * 
 */
package cn.ad.server.web.spring.service.components.adtraffic;

import cn.ad.server.common.utils.EncodeUtils;
import cn.ad.server.web.exception.AdServerMessageException;
import cn.ad.server.web.spring.model.gdt.server.*;
import cn.ad.server.web.spring.model.sts.STSAdResponse;
import cn.ad.server.web.spring.service.actions.context.AdContext;
import cn.ad.server.web.spring.service.actions.handler.gdt.server.HttpConnectionManager;
import cn.ad.server.web.spring.service.components.http.client.AdHttpExecutor;
import cn.ad.server.web.spring.service.components.http.client.AdHttpGetRequest;
import cn.ad.server.web.spring.service.components.http.client.AdHttpResponse;
import cn.ad.server.web.spring.service.components.http.client.ResponseHandleException;
import cn.ad.server.web.spring.service.httpclient4.MapResponseHandler;
import cn.ad.server.web.utils.JsonUtils;
import cn.ad.server.domain.ad.*;
import cn.ad.server.domain.enumration.*;
import cn.ad.server.domain.enumration.OS;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 广点通流量分发接口实现
 * @author WU CHENGJUN
 *
 */
//@Component
@Component("trafficDispatcher-gdt")
public class GDTTrafficDispatcher implements ITrafficDispatcher {
	
	private static Logger logger = LoggerFactory.getLogger(GDTTrafficDispatcher.class);

    private final String impAddress = "http://v.gdt.qq.com/gdt_stats.fcg";

    private final String clickAddress = "http://c.gdt.qq.com/gdt_mclick.fcg";

    @Resource
    private String serverAddress;

    @Autowired
    private HttpConnectionManager httpConnectionManager;
    
    @Resource(name="httpClientExecutor")
    private AdHttpExecutor httpClientExecutor;
    
    @Resource(name="mapResponseHandler")
    private MapResponseHandler responseHandler;
	
	/* (non-Javadoc)
	 * @see cn.falconnect.adserver.adapter.ITrafficDispatcher#dispatch(cn.falconnect.adserver.adapter.TrafficRequest)
	 */
	@Override
	public TrafficResponse dispatch(TrafficRequest request) {
		AdContext adContext = (AdContext) request.getExParam("AdContext");
		STSAdResponse.AdSpace adSpace = (STSAdResponse.AdSpace) request.getExParam("STSAdResponse.AdSpace");
		AdSpace localAdSpace = (AdSpace) request.getExParam("AdSpace");
		ReturnCode returnCode = (ReturnCode) request.getExParam("ReturnCode");
//		STSAdResponse.AdSpace reAdspace = doRequest(adContext, adSpace, localAdSpace, returnCode);
		STSAdResponse.AdSpace reAdspace = request(adContext, adSpace, localAdSpace, returnCode);
		TrafficResponse response = new TrafficResponse();
		response.addExParam("STSAdResponse.AdSpace", reAdspace);
		return response;
	}
	
	
	  /**
     * 拼装请求参数
     *
     * @param adContext
     * @param returnCode
     * @return
     */
    private Map<String, Object> compositeAdSpace(AdContext adContext, AdSpace localAdSpace, ReturnCode returnCode) {

        Map<String, Object> allParam = new LinkedHashMap<>();
        Map<String, Object> param = new LinkedHashMap<>();
        Map<String, String> specMap = new LinkedHashMap<>();
        param.put("adposcount", "1");
        param.put("count", "1");

        List<AdSpaceAdxMap> adSpaceAdxMaps = localAdSpace.getAdSpaceAdxMaps();
        if (!CollectionUtils.isEmpty(adSpaceAdxMaps)) {
            //FIXME 改成支持数组的请求 且支持计数
            AdSpaceAdxMap adSpaceAdxMap = adSpaceAdxMaps.get(0);

            if (adSpaceAdxMap.getStatus() == 0) {
                throw new AdServerMessageException(returnCode);
            }

            if(AdFormat.adFormatMap.get(localAdSpace.getAdFormat().getValue()) == AdFormat.AD_NATIVE){
                param.put("posw", 0);
                param.put("posh", 0);
            } else {
                if (adSpaceAdxMap.getWidth() > 0) {
                    param.put("posw", adSpaceAdxMap.getWidth());
                    param.put("posh", adSpaceAdxMap.getHeight());
                } else {
                    param.put("posw", adContext.getAdSpaceWidth());
                    param.put("posh", adContext.getAdSpaceHeight());
                }
            }

            String specMapString = adSpaceAdxMap.getSpecMap();
            if (StringUtils.isNotEmpty(specMapString)) {
                for (String spec : specMapString.split(",")) {
                    String[] split = spec.split(":");
                    specMap.put(split[0], split[1]);
                }

                allParam.put("spec", specMap);
            }

            param.put("posid", adSpaceAdxMap.getAdxSpaceId());
            param.put("charset", "utf8");
            param.put("datafmt", "json");

            //广告请求扩展内容
            GdtServerExt gdtServerExt = new GdtServerExt();
            GdtServerReq gdtServerReq = new GdtServerReq();

            // 设置request param
            gdtServerReq.setApiver("2.0");

            //AppId
            gdtServerReq.setAppid(adSpaceAdxMap.getAppId());
            gdtServerReq.setC_os(OS.osMap.get(adContext.getOs()).getName());
            if (adContext.getMuniqueType() == MUniqueType.IMEI.getValue()) {
                if (StringUtils.isNotEmpty(adContext.getImeiori())) {
                    gdtServerReq.setMuid(EncodeUtils.MD5(adContext.getImeiori().toLowerCase()));
                } else {
                    gdtServerReq.setMuid(adContext.getImei());
                }
                gdtServerReq.setMuidtype(GDTMuniqueType.IMEI.getValue());
            } else if (adContext.getMuniqueType() == MUniqueType.IDFA.getValue()) {
                gdtServerReq.setMuidtype(GDTMuniqueType.IFA.getValue());
                gdtServerReq.setMuid(adContext.getIdfa());
            } else if (adContext.getMuniqueType() == MUniqueType.MAC.getValue()) {
                //优先imei传递
                if (StringUtils.isNotEmpty(adContext.getImeiori())) {
                    gdtServerReq.setMuid(EncodeUtils.MD5(adContext.getImeiori().toLowerCase()));
                    gdtServerReq.setMuidtype(GDTMuniqueType.IMEI.getValue());
                } else {
                    gdtServerReq.setMuid(adContext.getMac());
                    gdtServerReq.setMuidtype(GDTMuniqueType.MAC.getValue());
                }
            } else {
                throw new AdServerMessageException(returnCode);
            }

            //FIXME 填了设备后就不能出广告了
            if (adContext.getBrand() != null) {
                gdtServerReq.setC_device(adContext.getBrand());
            } else if (adContext.getModel() != null) {
                gdtServerReq.setC_device(adContext.getModel());
            } else {
                gdtServerReq.setC_device("unknown");
            }

            //App包名
            gdtServerReq.setC_pkgname(adSpaceAdxMap.getPackageName());
            gdtServerReq.setPostype(GDTServerAdFormat.AD_FORMAT_MAP.get(AdFormat.adFormatMap.get(adContext.getAdFormat())));
            gdtServerReq.setConn(adContext.getConnType());
            gdtServerReq.setCarrier(adContext.getMob_ops());
            if (adContext.getScreenOritent() == Orientation.CROSS.ordinal()) {
                gdtServerReq.setC_w(adContext.getScreenHeight());
                gdtServerReq.setC_h(adContext.getScreenWidth());
            } else {
                gdtServerReq.setC_w(adContext.getScreenWidth());
                gdtServerReq.setC_h(adContext.getScreenHeight());
            }
            gdtServerReq.setRemoteip(adContext.getIp());
            if (adContext.getLatitude() != 0.0 && adContext.getLongitude() != 0.0) {
                gdtServerReq.setLat(new BigDecimal(adContext.getLatitude() * 1000000).intValue());
                gdtServerReq.setLng(new BigDecimal(adContext.getLongitude() * 1000000).intValue());
            }

            gdtServerReq.setUseragent(adContext.getUa());
            gdtServerReq.setReferer(adContext.getReferer());
            gdtServerReq.setC_osver(adContext.getOsVersion());
            if (StringUtils.isNotEmpty(adContext.getImeiori())) {
                gdtServerReq.setImei(EncodeUtils.MD5(adContext.getImeiori().toLowerCase()));
            }
            if (adContext.getMac() != null) {
                gdtServerReq.setMac(adContext.getMac());
            }
            if (adContext.getAndroidId() != null) {
                gdtServerReq.setAid(adContext.getAndroidId());
            }
            if (adContext.getAaid() != null) {
                gdtServerReq.setAaid(adContext.getAaid());
            }
            if (adContext.getIdfa() != null) {
                gdtServerReq.setIdfa(adContext.getIdfa());
            }

            gdtServerExt.setReq(gdtServerReq);
            param.put("ext", gdtServerExt);
        } else {
            //处理异常
            throw new AdServerMessageException(returnCode);
        }

        allParam.put("param", param);

        return allParam;
    }

    /**
     * gdt广告请求
     * @deprecated 此方法不推荐使用。{@link GDTTrafficDispatcher#doRequest(AdContext, cn.ad.server.web.spring.model.sts.STSAdResponse.AdSpace, AdSpace, ReturnCode)}
     * @param adContext
     * @param adSpace
     * @param localAdSpace
     * @param returnCode
     * @return
     */
    private STSAdResponse.AdSpace request(AdContext adContext, STSAdResponse.AdSpace adSpace, AdSpace localAdSpace, ReturnCode returnCode) {

        //设置转发失败
        //FIXME 修改adContext 不应该在跨模块之间实现传入参数值的修改
        adContext.setForwardType(2);
        Map<String, Object> allParam = compositeAdSpace(adContext, localAdSpace, returnCode);
        Map<String, Object> param = (Map<String, Object>)allParam.get("param");
        Map<String, String> specMap = (Map<String, String>)allParam.get("spec");

        CloseableHttpClient httpClient = httpConnectionManager.getHttpClient();
        HttpGet httpGet = new HttpGet("http://mi.gdt.qq.com/gdt_mview.fcg?" + generateParam(param));
        InputStream ins = null;
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            ins = response.getEntity().getContent();

            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
                //拼接返回参数
                Map result = JsonUtils.toObject(ins, Map.class);
                if ((int) result.get("ret") == 0) {
                    Object imp = ((Map) result.get("data")).get(param.get("posid"));
                    if (imp != null) {
                        Map impMap = (Map) imp;
                        List impList = (List) impMap.get("list");
                        if (!CollectionUtils.isEmpty(impList)) {
                            Map impEntity = (Map) impList.get(0);
                            STSAdResponse.Creative creative = new STSAdResponse.Creative();
                            creative.setImpression(new String[]{compositeImpUrl((String) impEntity.get("viewid"), param)});
                            STSAdResponse.Event event = new STSAdResponse.Event();
                            event.setEventKey(ActionType.LINK_JUMP.getValue());
                            if (OS.osMap.get(adContext.getOs()) == OS.IOS) {
                                event.setEventValue(impEntity.get("rl") + "&acttype=0&s={\"down_x\":-999,\"down_y\":-999,\"up_x\":-999,\"up_y\":-999}");
                            } else {
                                event.setEventValue(impEntity.get("rl") + "&acttype=&s={\"down_x\":-999,\"down_y\":-999,\"up_x\":-999,\"up_y\":-999}");
                            }
                            creative.setEvent(new STSAdResponse.Event[]{event});
                            creative.setClick(new String[]{compositeLocalClickUrl(adContext)});
                            creative.setAdmark("http://alicdn.lieying.cn/sentshow/imgs/tmp/tsa_ad_logo.png");
                            switch (AdFormat.adFormatMap.get(adContext.getAdFormat())) {

                                case BANNER:
                                case TABLE_SCREEN:
                                case OPEN_SCREEN:
                                    STSAdResponse.Banner banner = new STSAdResponse.Banner();
                                    banner.setWidth((int) param.get("posw"));
                                    banner.setHeight((int) param.get("posh"));
                                    String imgUrl = (String) impEntity.get("img");
                                    if (StringUtils.isEmpty(imgUrl)) {
                                        //处理异常
                                        throw new AdServerMessageException(returnCode);
                                    } else {
                                        banner.setCreativeUrl(imgUrl);
                                    }
                                    banner.setType(MaterialFormat.JPG.getName());
                                    creative.setBanner(banner);
                                    break;
                                case VIDEO:
                                    break;
                                case AD_NATIVE:
                                    //原生读取广告位上面的模版
                                    List<AdTemplateSpec> adTemplateSpecs = localAdSpace.getAdTemplate().getAdTemplateSpecs();
                                    STSAdResponse.AdNative[] adNatives = new STSAdResponse.AdNative[adTemplateSpecs.size()];
                                    STSAdResponse.AdNative adNative = null;
                                    for (int j = 0; j < adTemplateSpecs.size(); j++) {
                                        AdTemplateSpec adTemplateSpec = adTemplateSpecs.get(j);
                                        adNative = new STSAdResponse.AdNative();
                                        adNative.setTemplateId(adTemplateSpec.getAdTemplateId());
                                        adNative.setIndex(adTemplateSpec.getIndex());
                                        adNative.setIndexValue(adTemplateSpec.getIndexValue());
                                        adNative.setRequiredField(adTemplateSpec.getElementType());
                                        adNative.setRequiredValue((String) impEntity.get(specMap.get(adTemplateSpec.getIndexValue())));
                                        //暂时写死成jpg，后期可以改成截取url的后缀
                                        if (adTemplateSpec.getElementType() == ElementType.IMAGE.getValue()) {
                                            adNative.setType(MaterialFormat.JPG.getName());
                                        } else if (adTemplateSpec.getElementType() == ElementType.TEXT.getValue()) {
                                            adNative.setType(MaterialFormat.TEXT.getName());
                                        }
                                        adNatives[j] = adNative;
                                    }
                                    creative.setAdNative(adNatives);
                                    break;
                                default:
                                    break;
                            }

                            adSpace.setCreative(new STSAdResponse.Creative[]{creative});
                        } else {
                            //处理异常
                            throw new AdServerMessageException(returnCode);
                        }
                    }
                } else {
                    //处理异常
                    throw new AdServerMessageException(returnCode);
                }
            } else {
                //处理异常
                throw new AdServerMessageException(returnCode);
            }
        } catch (IOException e) {
            logger.error("http://mi.gdt.qq.com/gdt_mview.fcg request error");
            //处理异常
            throw new AdServerMessageException(returnCode);
        }

        adContext.setForwardType(1);
        return adSpace;
    }
    
    /**
     * gdt广告请求
     * 
     * @param adContext
     * @param adSpace
     * @param localAdSpace
     * @param returnCode
     * @return
     */
    private STSAdResponse.AdSpace doRequest(AdContext adContext, STSAdResponse.AdSpace adSpace, AdSpace localAdSpace, ReturnCode returnCode) {

        Map<String, Object> param = compositeAdSpace(adContext, localAdSpace, returnCode);
        //设置转发失败
      //FIXME 修改adContext 不应该在跨模块之间实现传入参数值的修改
        adContext.setForwardType(2);
        try {
        	AdHttpGetRequest httpGetReq = new AdHttpGetRequest();
        	//httpGetReq.setEventType(TrafficEventType.TRAFFICGDT.getValue());
        	httpGetReq.setUrl("http://mi.gdt.qq.com/gdt_mview.fcg?" + generateParam(param));
        	AdHttpResponse<Map> httpResponse = httpClientExecutor.execute(httpGetReq, responseHandler);
            if (HttpStatus.SC_OK == httpResponse.getStatusCode()) {
                //拼接返回参数
                Map result = httpResponse.getResult();
                if (result!=null&&(int) result.get("ret") == 0) {
                    Object imp = ((Map) result.get("data")).get(param.get("posid"));
                    if (imp != null) {
                        Map impMap = (Map) imp;
                        List impList = (List) impMap.get("list");
                        if (!CollectionUtils.isEmpty(impList)) {
                            Map impEntity = (Map) impList.get(0);
                            STSAdResponse.Creative creative = new STSAdResponse.Creative();
                            creative.setImpression(new String[]{compositeImpUrl((String) impEntity.get("viewid"), param)});
                            STSAdResponse.Event event = new STSAdResponse.Event();
                            event.setEventKey(ActionType.LINK_JUMP.getValue());
                            if (OS.osMap.get(adContext.getOs()) == OS.IOS) {
                                event.setEventValue(impEntity.get("rl") + "&acttype=0&s={\"down_x\":-999,\"down_y\":-999,\"up_x\":-999,\"up_y\":-999}");
                            } else {
                                event.setEventValue(impEntity.get("rl") + "&acttype=&s={\"down_x\":-999,\"down_y\":-999,\"up_x\":-999,\"up_y\":-999}");
                            }
                            creative.setEvent(new STSAdResponse.Event[]{event});
                            creative.setClick(new String[]{compositeLocalClickUrl(adContext)});
                            creative.setAdmark("http://alicdn.lieying.cn/sentshow/imgs/tmp/tsa_ad_logo.png");
                            switch (AdFormat.adFormatMap.get(adContext.getAdFormat())) {

                                case BANNER:
                                case TABLE_SCREEN:
                                case OPEN_SCREEN:
                                    STSAdResponse.Banner banner = new STSAdResponse.Banner();
                                    banner.setWidth((int) param.get("posw"));
                                    banner.setHeight((int) param.get("posh"));
                                    String imgUrl = (String) impEntity.get("img");
                                    if (StringUtils.isEmpty(imgUrl)) {
                                        //处理异常
                                        throw new AdServerMessageException(returnCode);
                                    } else {
                                        banner.setCreativeUrl(imgUrl);
                                    }
                                    banner.setType(MaterialFormat.JPG.getName());
                                    creative.setBanner(banner);
                                    break;
                                case VIDEO:
                                case AD_NATIVE:
                                default:
                                    break;
                            }

                            adSpace.setCreative(new STSAdResponse.Creative[]{creative});
                        } else {
                            //处理异常
                            throw new AdServerMessageException(returnCode);
                        }
                    }
                } else {
                    //处理异常
                    throw new AdServerMessageException(returnCode);
                }
            } else {
                //处理异常
                throw new AdServerMessageException(returnCode);
            }
        } catch (IOException | ResponseHandleException e) {
        	if(!(e instanceof SocketTimeoutException) ){
        		logger.error("http://mi.gdt.qq.com/gdt_mview.fcg request error",e);
        	}
            //处理异常
            throw new AdServerMessageException(returnCode);
        }

        adContext.setForwardType(1);
        return adSpace;
    }
    
    

    private String generateParam(Map<String,Object> map) {
        List<String> params = new ArrayList<>();
        if (map != null && map.size() > 0) {
            for (Map.Entry entry : map.entrySet()) {
                if (entry.getValue() instanceof String || entry.getValue() instanceof Integer) {
                    params.add(String.format("%s=%s", entry.getKey(), entry.getValue()));
                } else {
                    try {
                        params.add(String.format("%s=%s", entry.getKey(), URLEncoder.encode(JsonUtils.toJson(entry.getValue()),"utf-8")));
                    } catch (UnsupportedEncodingException e) {
                        logger.error("urlencode error");
                    }
                }
            }
        }
        return StringUtils.join(params, "&");
    }

    private String compositeImpUrl(String viewId, Map param) {

        String impUrlFormat = impAddress + "?count=%s&viewid0=%s";
        return String.format(impUrlFormat, param.get("count"), viewId);
    }

    private String compositeLocalClickUrl(AdContext adContext) {
        String impUrlFormat = serverAddress + "redirect.html?version=%s&aid=%s&sid=%s";
        return String.format(impUrlFormat, adContext.getVersion(), adContext.getAdSpaceId(), 0);
    }
}
